package com.slimerevenge.view;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.maps.MapProperties;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapRenderer;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.math.Circle;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.Shape;
import com.slimerevenge.model.Block;
import com.slimerevenge.model.Level;
import com.slimerevenge.model.Player;

public class WorldRenderer implements ContactListener {
	private Level level;
	private OrthographicCamera cam;

	private boolean debug = false;

	private static final float CAMERA_WIDTH = 10f;
	private static final float CAMERA_HEIGHT = 7f;
	public static final float PIXELS_PER_METER = 60.0f;
	public static final float PIXELS_PER_TILE = 32.0f;

	private Texture playerTexture;
	private Texture blockTexture;

	private SpriteBatch spriteBatch;

	private int width;
	private int height;

	private float ppuX; // pixels per unit on the X axis
	private float ppuY; // pixels per unit on the Y axis

	/** for debug rendering **/
	ShapeRenderer debugRenderer = new ShapeRenderer();
	
	private TiledMap map;
	private TiledMapRenderer maprenderer;
	private OrthographicCamera camera;
	private BitmapFont font;
	private SpriteBatch batch;

	public void setSize(int w, int h) {
		this.width = w;
		this.height = h;
		System.out.println("Width:"+w+" Height:"+h);
		ppuX = (float) width / CAMERA_WIDTH;
		ppuY = (float) height / CAMERA_HEIGHT;
		//level.getPlayer().refreshPlayer();
	}

	public WorldRenderer(Level level, boolean debug) {
		float w = Gdx.graphics.getWidth();
		float h = Gdx.graphics.getHeight();

		camera = new OrthographicCamera();
		//camera.setToOrtho(false, (w / h) * 10, 10);
		//camera.update();

		//cameraController = new CameraController(camera);
		//Gdx.input.setInputProcessor((InputProcessor) cameraController);

		font = new BitmapFont();
		batch = new SpriteBatch();

		map = level.getMap();
		
		maprenderer = new OrthogonalTiledMapRenderer(map, 1f/PIXELS_PER_TILE);
		this.debug = debug;
		this.level = level;

		this.camera = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
		//this.camera.position.set(5, 3.5f, 0);
		this.camera.update();
		spriteBatch = new SpriteBatch();
		loadTextures();
	}

	private void loadTextures() {
		playerTexture = level.getPlayer().getTexture();
		//playerTexture = new Texture(Gdx.files.internal("sprite/slime.png"));
		blockTexture = new Texture(Gdx.files.internal("sprite/block.png"));
	}

	public void render() {
		
		/**
		 * Have box2d update the positions and velocities (and etc) of all
		 * tracked objects. The second and third argument specify the number of
		 * iterations of velocity and position tests to perform -- higher is
		 * more accurate but is also slower.
		 */
		level.getWorld().step(Gdx.app.getGraphics().getDeltaTime(), 3, 3);
		
		maprenderer.setView(camera);
		maprenderer.render();
		
		spriteBatch.setProjectionMatrix(camera.combined);
		spriteBatch.begin();
		drawPlayer();
		drawBlocks();
		spriteBatch.end();
		
		Gdx.gl.glClearColor(0.55f, 0.55f, 0.55f, 1f);
		//Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		
		if(camera.position.x < level.getPlayer().getPosition().x-0.3f || camera.position.x > level.getPlayer().getPosition().x+0.3f )
			camera.position.x += (level.getPlayer().getPosition().x-camera.position.x) / 20f;
		
		if(camera.position.y < level.getPlayer().getPosition().y-0.3f|| camera.position.y > level.getPlayer().getPosition().y+0.3f )
			camera.position.y += (level.getPlayer().getPosition().y-camera.position.y) / 20f;

		
		//Repair player's original look
		/*int subBodyArrSize = level.getPlayer().getSubBodyArray().size;
		for(int i =0; i<subBodyArrSize;i++)
		{
			Body subBody = level.getPlayer().getSubBodyArray().get(i);
	        float orisubx = MathUtils.cosDeg(360/subBodyArrSize*i)/4f;
	        float orisuby = MathUtils.sinDeg(360/subBodyArrSize*i)/4f;
			float newsubx = subBody.getPosition().x;
			float newsuby = subBody.getPosition().y;
			
			//if(subBody.getPosition().dst(level.getPlayer().getPosition())==0);
			if(subBody.getPosition().dst2(level.getPlayer().getPosition())>0.3)
			//if(subBodyInv.getPosition().dst(subBody.getPosition())<0 || subBodyInv.getPosition().dst(subBody.getPosition())>1)
			{
				
				newsubx = (subBody.getPosition().x+orisubx);//10f;
				newsuby = (subBody.getPosition().y+orisuby);//10f;
				
				subBody.setTransform(newsubx,newsuby,subBody.getAngle());
			}
			
			
			
			
			if(subBody.getPosition().x<orisubx-0.01f || subBody.getPosition().x>orisubx+0.01f)
			{
				subBody.setTransform(subBody.getPosition().x+orisubx/5f, subBody.getPosition().y, subBody.getAngle());
			}
			if(subBody.getPosition().y<orisuby-0.01f || subBody.getPosition().y>orisuby+0.01f)
			{
				subBody.setTransform(subBody.getPosition().x, subBody.getPosition().y+orisuby/5f, subBody.getAngle());
			}
		}*/
		
		/**
		 * Ensure that the camera is only showing the map, nothing outside.
		 */
		
		/*if (camera.position.x < Gdx.graphics.getWidth() / 2) {
			camera.position.x = Gdx.graphics.getWidth() / 2;
		}
		if (camera.position.x >= mp.get("width",Integer.class) * mp.get("tilewidth",Integer.class)
				- Gdx.graphics.getWidth() / 2) {
			camera.position.x = mp.get("width",Integer.class)
					- Gdx.graphics.getWidth() / 2;
		}

		if (camera.position.y < Gdx.graphics.getHeight() / 2) {
			camera.position.y = Gdx.graphics.getHeight() / 2;
		}
		if (camera.position.y >= mp.get("height",Integer.class) * mp.get("tileheight",Integer.class)
				- Gdx.graphics.getHeight() / 2) {
			camera.position.y = mp.get("height",Integer.class)
					- Gdx.graphics.getHeight() / 2;
		}*/
		
		camera.update();
		
		//renderer.setView(camera);
		

		
		if (debug)
			drawDebug();
	}
	
	private void drawBlocks() {
		for (Block block : level.getBlocks()) {
			spriteBatch.draw(blockTexture, block.getPosition().x, block.getPosition().y, Block.SIZE , Block.SIZE );
			//spriteBatch.draw(blockTexture, block.getPosition().x * ppuX, block.getPosition().y * ppuY, Block.SIZE * ppuX, Block.SIZE * ppuY);
		}
	}

	private void drawPlayer() {
		Body player = level.getPlayer().getBody();
		spriteBatch.draw(playerTexture, player.getPosition().x - Player.SIZE/2,
				player.getPosition().y - Player.SIZE/2, Player.SIZE, Player.SIZE);
//		spriteBatch.draw(playerTexture, player.getPosition().x * ppuX,
//				player.getPosition().y * ppuY, Player.SIZE * ppuX, Player.SIZE
//						* ppuY);
	}

	private void drawDebug() {
		batch.begin();
		font.draw(batch, "FPS: " + Gdx.graphics.getFramesPerSecond(), 10, 20); 
		batch.end();
		
		// render blocks
		debugRenderer.setProjectionMatrix(camera.combined);
		debugRenderer.begin(ShapeType.Line);
		for (Block block : level.getBlocks()) {
			Rectangle rect = block.getBounds();
			float x1 = block.getPosition().x + rect.x;
			float y1 = block.getPosition().y + rect.y;
			debugRenderer.setColor(new Color(1, 0, 0, 1));
			debugRenderer.rect(x1, y1, rect.width, rect.height);
		}
		// render Player
		Player player = level.getPlayer();
		//Rectangle rect = player.getBounds();
		float x1 = player.getBody().getPosition().x;
		float y1 = player.getBody().getPosition().y;
		debugRenderer.setColor(new Color(0, 1, 0, 1));
		debugRenderer.circle(x1,y1, Player.SIZE/8f, 8); 

		for(int i =0; i<player.getSubBodyArray().size;i++)
		{
			float x2 = player.getSubBodyArray().get(i).getPosition().x;
			float y2 = player.getSubBodyArray().get(i).getPosition().y;
			//debugRenderer.circle(x2,y2,  Player.SIZE/8f, 8); 
		}
		for(int i =0; i<player.getJointsDefArr().size;i++)
		{
			Vector2 bdLineA = new Vector2(player.getJointsDefArr().get(i).bodyA.getPosition().x,player.getJointsDefArr().get(i).bodyA.getPosition().y);
			Vector2 bdLineB = new Vector2(player.getJointsDefArr().get(i).bodyB.getPosition().x,player.getJointsDefArr().get(i).bodyB.getPosition().y);
			debugRenderer.line(bdLineA, bdLineB);
		}
		debugRenderer.end();
	}

	@Override
	public void beginContact(Contact contact) {
		// TODO Auto-generated method stub
		/*String a = (String) contact.getFixtureA().getUserData();
		String b = (String) contact.getFixtureB().getUserData();
		
		Gdx.app.log("TOUCHED ",b.toString());
		
		if( (a.equals("player") && b.equals("block")) ||
				(b.equals("player") && a.equals("block")) )
		{
			Gdx.app.log("TOUCHED A: ",contact.getFixtureA().getBody().getPosition().toString());
			Gdx.app.log("TOUCHED B: ",contact.getFixtureB().getBody().getPosition().toString());
		}*/
		
	}

	@Override
	public void endContact(Contact contact) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void preSolve(Contact contact, Manifold oldManifold) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void postSolve(Contact contact, ContactImpulse impulse) {
		// TODO Auto-generated method stub
		
	}
}
